#include <GL/glut.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <asm/types.h>    /* for videodev2.h */
#include <linux/videodev2.h>
#include "helpers.h"
#include "shaderutils.h"

GLuint shaderFilter = 0;
GLuint shaderImmortal = 0;
GLuint shaderEdge = 0;
GLuint filterTextureLocation = 0;
GLuint filterTextureLocation2 = 0;
GLuint immortalTextureLocation = 0;
GLuint immortalTextureLocation2 = 0;
GLuint edgeTextureLocation = 0;
GLuint edgeTextureLocation2 = 0;
GLuint stateLocation = 0;
GLuint pic, pic2, fbotex,  anothertex;
int hell = 0;
int ind = 0;
float ind2 = 0.0;
int ball = 0;
int drawingBall = 0;
int button = 0;
int drawingButton = 0;
int beingHit = 0;
GLfloat state = 0.0;
int buttonHit = 0;

CvCapture* g_Capture;

int timesHit = 0;
typedef struct Color {
  int r;
  int g;
  int b;
} Color;

float ballPos;
int noOfBalls  = -1;

float clamp(float x) { 
  if (x < 0) {
    return 0;
  }
  else if (x > 1) {
    return 1;
  }
  else {
    return x;
  }
}
int drawButton(IplImage *image, int pos) {
  glPushMatrix();
  glColor4f(1.0, 1.0, 1.0, 1.0);
  float buttonPos = 0.0;
  float buttonY = 0.8;
  float buttonRadius = 0.1;
  if (pos == 0) {  //Upper left
    buttonPos = -0.8;
    buttonY = 0.8;
  }
  else if (pos == 1) { //Upper right
    buttonPos = 0.8;
    buttonY = 0.8;
  }
  else if (pos == 2) { //Lower left
    buttonPos = -0.8;
    buttonY = -0.8;
  }
  else { //(pos == 3) Lower right
    buttonPos = 0.8;
    buttonY = -0.8;
  }
  glTranslatef(buttonPos,buttonY,0.0);
  glutSolidSphere(buttonRadius, 15, 15);
  int hit = 0;
  CvScalar s;
  int i, j;
  int height = image->height;
  int width = image->width;
  int starti, stopi, startj, stopj;
  if (pos == 0) { //Upper left
    starti = 0;
    stopi = height/4;
    startj = 0;
    stopj = width/4;
  }
  else if (pos == 1) { //Upper right
    starti = 0;
    stopi = height/4;
    startj = 3*(width/4);
    stopj = width;
  }
  else if (pos == 2) { //Lower left
    starti = 3*(height/4);
    stopi = height;
    startj = 0;
    stopj = width/4;
  }
  else { //(pos == 3) Lower right
    starti = 3*(height/4);
    stopi = height;
    startj = 3*(width/4);
    stopj = width;
  }

  for (i = starti; i < stopi; i++) {
    for(j = startj; j < stopj; j++) {
      s=cvGet2D(image,i,j); // get the (i,j) pixel value	
      if ((s.val[1]/s.val[2] < 1.0 || s.val[1]/s.val[0] < 1.0)) {
	//X-position of current pixel
	float xPos = ((float)j - 320.0) * (1.0/320.0);
	//Y-position of current pixel
	float yPos = ((float)i - 240.0) * (1.0/240.0);
	//Check distance to button
	if((buttonPos - xPos) < buttonRadius && 
	   (buttonPos - xPos) > -buttonRadius && 
	   (-yPos < buttonY + buttonRadius && -yPos > buttonY - buttonRadius)) {
	  hit += 1;
	  //printf("hit at %f, %f \n", xPos, yPos);
	}
      }
    }
  }
  if (hit > 50) {
    printf("Hit button! \n");
    button = 0;
    drawingButton = 0;
    hit = 0;
    glPopMatrix();
    return pos;
  }
  glPopMatrix();
  return -1;
}


void drawBall(IplImage *image) {
  glPushMatrix();
  glColor4f(1.0, 1.0, 1.0, 1.0);
  float ballY = 0.0;
  float ballRadius = 0.1;
  glTranslatef(-1.0+(0.1*ballPos),ballY,0.0);
  glutSolidSphere(ballRadius, 15, 15);
  int hit = 0;    
  if (ballPos < 20.0) {  //Behövs lite till, är 20 för att kunna debugga
    ballPos += 0.1;
    CvScalar s;
    int i, j;
    int height = image->height;
    int width = image->width;
    //  printf("Width: %i   Height: %i\n", width, height);
    for (i = 0; i < height; i++) {
      for(j = 0; j < width; j++) {
	s=cvGet2D(image,i,j); // get the (i,j) pixel value		
	if (s.val[1]/s.val[2] < 1.0 || s.val[1]/s.val[0] < 1.0) {

	  //X-position of current pixel
	  float xPos = ((float)j - 320.0) * (1.0/320.0);
	  //Y-position of current pixel
	  float yPos = ((float)i - 240.0) * (1.0/240.0);
	  //Check distance to ball
	  if(((-1.0+(0.1*ballPos)) - xPos) < ballRadius && 
	     ((-1.0+(0.1*ballPos)) - xPos) > -ballRadius && 
	     (-yPos < ballY + ballRadius && -yPos > ballY - ballRadius)) {
	    hit += 1;
	    //printf("hit at %f, %f \n", (-1.0+(0.1*ballPos))- xPos, xPos);
	  }
	}
	else {
	  /*s.val[0]=0;  //Blue
	  s.val[1]=0;    //Green
	  s.val[2]=256;    //Red*/
	}
      }
    }
    if (hit > 50 && hell != 1) {
      timesHit += 1;
      printf("Hit! %i \n", timesHit);
      hell = 2; 
      ball = 0;
      drawingBall = 0;
      ballPos = 1.0;
      hit = 0;
      beingHit = 25;
    }
    else if (hell != 1){
      hell = 0;
    }
  }
  else {
    ball = 0;
    drawingBall = 0;
    ballPos = 1.0;
    hit = 0;
    if (hell != 1) {
      hell = 0;
    }
  }
  glPopMatrix();
}

void HandleKeyboard(unsigned char key,int x, int y)
{
  switch (key) {
  case 'a':
    if (hell == 2) {
      hell = 0;
    }
    else {
      hell += 1;
    }
    break;
  case 'b':
    ballPos = 1.0;
    ball = 1 - ball;
    break;
  case 'k':
    if (button > 3) {
      button = 0;
    }
    else {
      button++;// = 1 - button;
    }
    break;
  case 27:
    exit(0);
    break;
  }
glutPostRedisplay();

}

GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 };


CvSize size;
CvVideoWriter *videoWriter;

void init()
{

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  // Set default material properties
  GLfloat mat_shininess[] = { 50.0 };
  GLfloat mat_diffuseColor[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat mat_specularColor[] = { 1.0, 1.0, 1.0, 1.0 };

  glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuseColor);
  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specularColor);
  GLfloat ambientColor [] = { 1.0, 1.0, 1.0, 0.9 };
  GLfloat diffuseColor[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat specularColor[] = { 1.0, 1.0, 1.0, 0.9 };
  glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor);
  glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor);
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_NORMALIZE);
 
  glEnable(GL_DEPTH_TEST);

  pic2 = loadTexture("textures/weather.jpg");

  // Create vertex shader
  GLuint vertexShader = createShaderFromFile(GL_VERTEX_SHADER, "vertexShader.vs");
  // Create fragment shader
  GLuint fragmentShader = createShaderFromFile(GL_FRAGMENT_SHADER, "filter.fs");
  // Create shader program
  shaderFilter = createShaderProgram(vertexShader, fragmentShader);
  fragmentShader = createShaderFromFile(GL_FRAGMENT_SHADER, "immortal.fs");
  shaderImmortal = createShaderProgram(vertexShader, fragmentShader);
  fragmentShader = createShaderFromFile(GL_FRAGMENT_SHADER, "edge.fs");
  shaderEdge = createShaderProgram(vertexShader, fragmentShader);
  filterTextureLocation = getUniformLocation(shaderFilter, "texture");
  filterTextureLocation2 = getUniformLocation(shaderFilter, "texture2");
  immortalTextureLocation = getUniformLocation(shaderImmortal, "texture");
  immortalTextureLocation2 = getUniformLocation(shaderImmortal, "texture2");
  edgeTextureLocation = getUniformLocation(shaderImmortal, "texture");
  edgeTextureLocation2 = getUniformLocation(shaderImmortal, "texture2");
  stateLocation = getUniformLocation(shaderFilter, "state");
}

void display()
{ 
  glClearColor(1.0, 1.0, 1.0, 1);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  
  glEnable(GL_TEXTURE_2D);

  //------------------------------------------------
  //Webcam code
  //------------------------------------------------
  
  // Capture next frame
  IplImage *image = cvQueryFrame(g_Capture);    
  cvFlip(image, NULL, 1);

  if (ball == 1 || drawingBall == 1) {
    drawBall(image);
  }
  if (button > 0 || drawingButton == 1) {
    int button1 = drawButton(image, 0);
    int button2 = drawButton(image, 1);
    int button3 = drawButton(image, 2);
    int button4 = drawButton(image, 3);
    if (button1 != -1) {
      pic2 = loadTexture("textures/weather.jpg");
    }
    else if (button2 != -1) {
      pic2 = loadTexture("textures/hell.jpg");
    }
    else if (button3 != -1) {
      pic2 = loadTexture("textures/heaven.jpg");
    }

  }
  int hithell = 0;
  if (beingHit > 0) {
    hell = 2;
    beingHit--;
  }
  else if (hell == 2){
    hell = 0;
  }

  /*printf("\nBrightness: %f\n", cvGetCaptureProperty(g_Capture, CV_CAP_PROP_BRIGHTNESS));
 printf("Contrast: %f\n", cvGetCaptureProperty(g_Capture, CV_CAP_PROP_CONTRAST));
  printf("Gain: %f\n", cvGetCaptureProperty(g_Capture, CV_CAP_PROP_GAIN));
  cvSetCaptureProperty(g_Capture, CV_CAP_PROP_SATURATION, 0.3);
   printf("Saturation: %f\n", cvGetCaptureProperty(g_Capture, CV_CAP_PROP_SATURATION));*/
 
  // Convert to RGB
  cvCvtColor(image, image, CV_BGR2RGB);
 
  // Create Texture
  glBindTexture(GL_TEXTURE_2D, pic);// NULL = Empty texture
  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image->width, image->height, GL_RGB, GL_UNSIGNED_BYTE, image->imageData);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  
  //------------------------------------------------
  //
  //------------------------------------------------

  //glActiveTexture(GL_TEXTURE0);
  if (hell == 0) {
    //  printf("Hell = 0\n");
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, pic2);
    glUseProgram(shaderFilter);
    glUniform1i(filterTextureLocation2, 1);
    glUniform1f(stateLocation, state);
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(filterTextureLocation, 0);
    }
  else if (hell == 1) {
    // printf("Hell = 1\n");
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, pic2);
    glUseProgram(shaderImmortal);
    glUniform1i(filterTextureLocation2, 1);
    glUniform1f(stateLocation, state);
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(filterTextureLocation, 0);
    }
  else if (hell == 2) {
    // printf("Hell = 2\n");
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, pic2);
    glUseProgram(shaderEdge);
    glUniform1i(edgeTextureLocation2, 1);
    glUniform1f(stateLocation, state);
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(edgeTextureLocation, 0);
    }



  
  //glBindTexture(GL_TEXTURE_2D, pic);
  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
  glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 0.0);
  glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 0.0);
  glTexCoord2f(1.0, 1.0); glVertex3f(1.0, -1.0, 0.0);
  glEnd();
  if (hell == 1) {
    glUseProgram(0);
  }
  glDisable(GL_TEXTURE_2D);
  //}
  glFrustum(-0.1, 0.1, -0.1, 0.1, 0.1, 700);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  // Swap front- and backbuffers

  glutSwapBuffers();
}

float hold1 = 0.0;
int hold2 = 0;



void idle()
{
  // As soon as the machine is idle, ask GLUT to trigger rendering of a new
  // frame
  glutPostRedisplay();
}
void reshape(GLsizei w, GLsizei h)
{	
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-0.5, 0.5, -0.5, 0.5, 0.5, 20); // left, right, bottom, top, near, far
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0, 0, 2.5, 0, 0, 0, 0, 1, 0); // camera, look-at-point, up-vector
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv);

  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
  glutInitWindowSize(800, 800);
  glutCreateWindow("BMC-FX");
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(90, 1, 0.01, 1000);
  glMatrixMode(GL_MODELVIEW);

  //--------------------------------------------
  //Initialize webcam
  //--------------------------------------------
  
  g_Capture = cvCreateCameraCapture(0);
  //g_Capture = cvCaptureFromAVI(argv[1]);
  assert(g_Capture);
  
  // printf("\nMode: %f\n", cvGetCaptureProperty(g_Capture, CV_CAP_PROP_BRIGHTNESS));
  //cvSetCaptureProperty(g_Capture, CV_CAP_PROP_GAIN, 0.5);
  //cvSetCaptureProperty(g_Capture, CV_CAP_PROP_BRIGHTNESS, 0.5);
  //cvSetCaptureProperty(g_Capture, CV_CAP_PROP_HUE, 50);

  //--------------------------------------------
  //
  //--------------------------------------------


  initHelperLibrary();
  init();
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutIdleFunc(idle);
  glutKeyboardFunc(HandleKeyboard);

  //glutGameModeString( "1024x768:32@75" ); //the settings for fullscreen mode
  //glutEnterGameMode(); 

  glutMainLoop();

  return 0;
}

